///  <summary>
///  Routines for detecting devices and receiving device notifications.
///  </summary>
  
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Runtime.InteropServices; 
using System.Windows.Forms;

namespace GenericHid
{
	sealed internal partial class DeviceManagement
	{
		///  <summary>
		///  Compares two device path names. Used to find out if the device name 
		///  of a recently attached or removed device matches the name of a 
		///  device the application is communicating with.
		///  </summary>
		///  
		///  <param name="m"> a WM_DEVICECHANGE message. A call to RegisterDeviceNotification
		///  causes WM_DEVICECHANGE messages to be passed to an OnDeviceChange routine.. </param>
		///  <param name="mydevicePathName"> a device pathname returned by 
		///  SetupDiGetDeviceInterfaceDetail in an SP_DEVICE_INTERFACE_DETAIL_DATA structure. </param>
		///  
		///  <returns>
		///  True if the names match, False if not.
		///  </returns>
		///  
		internal Boolean DeviceNameMatch(Message m, String mydevicePathName)
		{
			Int32 stringSize;
			DEV_BROADCAST_DEVICEINTERFACE_1 devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE_1();
			DEV_BROADCAST_HDR devBroadcastHeader = new DEV_BROADCAST_HDR();

			// The LParam parameter of Message is a pointer to a DEV_BROADCAST_HDR structure.

			Marshal.PtrToStructure(m.LParam, devBroadcastHeader);

			if ((devBroadcastHeader.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE))
			{
				// The dbch_devicetype parameter indicates that the event applies to a device interface.
				// So the structure in LParam is actually a DEV_BROADCAST_INTERFACE structure, 
				// which begins with a DEV_BROADCAST_HDR.

				// Obtain the number of characters in dbch_name by subtracting the 32 bytes
				// in the strucutre that are not part of dbch_name and dividing by 2 because there are 
				// 2 bytes per character.

				stringSize = System.Convert.ToInt32((devBroadcastHeader.dbch_size - 32) / 2);

				// The dbcc_name parameter of devBroadcastDeviceInterface contains the device name. 
				// Trim dbcc_name to match the size of the String.         

				devBroadcastDeviceInterface.dbcc_name = new Char[stringSize + 1];

				// Marshal data from the unmanaged block pointed to by m.LParam 
				// to the managed object devBroadcastDeviceInterface.

				Marshal.PtrToStructure(m.LParam, devBroadcastDeviceInterface);

				// Store the device name in a String.

				String DeviceNameString = new String(devBroadcastDeviceInterface.dbcc_name, 0, stringSize);

				// Compare the name of the newly attached device with the name of the device 
				// the application is accessing (mydevicePathName).
				// Set ignorecase True.

				if ((String.Compare(DeviceNameString, mydevicePathName, true) == 0))
				{
					return true;
				}
				else
				{
					return false;
				}
			}

			return false;
		}

		///  <summary>
		///  Use SetupDi API functions to retrieve the device path name of an
		///  attached device that belongs to a device interface class.
		///  </summary>
		///  
		///  <param name="myGuid"> an interface class GUID. </param>
		///  <param name="devicePathName"> a pointer to the device path name 
		///  of an attached device. </param>
		///  
		///  <returns>
		///   True if a device is found, False if not. 
		///  </returns>

		internal Boolean FindDeviceFromGuid(System.Guid myGuid, ref String[] devicePathName)
		{
			Int32 bufferSize = 0;
			IntPtr detailDataBuffer;
			Boolean deviceFound;
			IntPtr deviceInfoSet = new System.IntPtr();
			Boolean lastDevice = false;
			Int32 memberIndex = 0;
			SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
			Boolean success;

				// ***
				//  API function

				//  summary 
				//  Retrieves a device information set for a specified group of devices.
				//  SetupDiEnumDeviceInterfaces uses the device information set.

				//  parameters 
				//  Interface class GUID.
				//  Null to retrieve information for all device instances.
				//  Optional handle to a top-level window (unused here).
				//  Flags to limit the returned information to currently present devices 
				//  and devices that expose interfaces in the class specified by the GUID.

				//  Returns
				//  Handle to a device information set for the devices.
				// ***

				deviceInfoSet = SetupDiGetClassDevs(ref myGuid, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);

				deviceFound = false;
				memberIndex = 0;

				do
				{
					// Begin with 0 and increment through the device information set until
					// no more devices are available.

					// The cbSize element of the MyDeviceInterfaceData structure must be set to
					// the structure's size in bytes. 
					// The size is 28 bytes for 32-bit code and 32 bits for 64-bit code.

					MyDeviceInterfaceData.cbSize = Marshal.SizeOf(MyDeviceInterfaceData);

					// ***
					//  API function

					//  summary
					//  Retrieves a handle to a SP_DEVICE_INTERFACE_DATA structure for a device.
					//  On return, MyDeviceInterfaceData contains the handle to a
					//  SP_DEVICE_INTERFACE_DATA structure for a detected device.

					//  parameters
					//  DeviceInfoSet returned by SetupDiGetClassDevs.
					//  Optional SP_DEVINFO_DATA structure that defines a device instance 
					//  that is a member of a device information set.
					//  Device interface GUID.
					//  Index to specify a device in a device information set.
					//  Pointer to a handle to a SP_DEVICE_INTERFACE_DATA structure for a device.

					//  Returns
					//  True on success.
					// ***

					success = SetupDiEnumDeviceInterfaces
						(deviceInfoSet,
						IntPtr.Zero,
						ref myGuid,
						memberIndex,
						ref MyDeviceInterfaceData);

					// Find out if a device information set was retrieved.

					if (!success)
					{
						lastDevice = true;

					}
					else
					{
						// A device is present.

						// ***
						//  API function: 

						//  summary:
						//  Retrieves an SP_DEVICE_INTERFACE_DETAIL_DATA structure
						//  containing information about a device.
						//  To retrieve the information, call this function twice.
						//  The first time returns the size of the structure.
						//  The second time returns a pointer to the data.

						//  parameters
						//  DeviceInfoSet returned by SetupDiGetClassDevs
						//  SP_DEVICE_INTERFACE_DATA structure returned by SetupDiEnumDeviceInterfaces
						//  A returned pointer to an SP_DEVICE_INTERFACE_DETAIL_DATA 
						//  Structure to receive information about the specified interface.
						//  The size of the SP_DEVICE_INTERFACE_DETAIL_DATA structure.
						//  Pointer to a variable that will receive the returned required size of the 
						//  SP_DEVICE_INTERFACE_DETAIL_DATA structure.
						//  Returned pointer to an SP_DEVINFO_DATA structure to receive information about the device.

						//  Returns
						//  True on success.
						// ***                     

						success = SetupDiGetDeviceInterfaceDetail
							(deviceInfoSet,
							ref MyDeviceInterfaceData,
							IntPtr.Zero,
							0,
							ref bufferSize,
							IntPtr.Zero);

						// Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure using the returned buffer size.

						detailDataBuffer = Marshal.AllocHGlobal(bufferSize);

						// Store cbSize in the first bytes of the array. The number of bytes varies with 32- and 64-bit systems.
						
						Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
						
						// Call SetupDiGetDeviceInterfaceDetail again.
						// This time, pass a pointer to DetailDataBuffer
						// and the returned required buffer size.

						success = SetupDiGetDeviceInterfaceDetail
							(deviceInfoSet,
							ref MyDeviceInterfaceData,
							detailDataBuffer,
							bufferSize,
							ref bufferSize,
							IntPtr.Zero);

						// Skip over cbsize (4 bytes) to get the address of the devicePathName.

						IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);

						// Get the String containing the devicePathName.

						devicePathName[memberIndex] = Marshal.PtrToStringAuto(pDevicePathName);

						// Free the memory allocated previously by AllocHGlobal.

						Marshal.FreeHGlobal(detailDataBuffer);
						deviceFound = true;
					}
					memberIndex = memberIndex + 1;
				}
				while (!((lastDevice == true)));

				// ***
				//  API function

				//  summary
				//  Frees the memory reserved for the DeviceInfoSet returned by SetupDiGetClassDevs.

				//  parameters
				//  DeviceInfoSet returned by SetupDiGetClassDevs.

				//  returns
				//  True on success.
				// ***

				SetupDiDestroyDeviceInfoList(deviceInfoSet);

                Array.Resize<string>(ref devicePathName, memberIndex - 1);

				return deviceFound;
		}

		///  <summary>
		///  Requests to receive a notification when a device is attached or removed.
		///  </summary>
		///  
		///  <param name="devicePathName"> handle to a device. </param>
		///  <param name="formHandle"> handle to the window that will receive device events. </param>
		///  <param name="classGuid"> device interface GUID. </param>
		///  <param name="deviceNotificationHandle"> returned device notification handle. </param>
		///  
		///  <returns>
		///  True on success.
		///  </returns>
		///  
		internal Boolean RegisterForDeviceNotifications(String devicePathName, IntPtr formHandle, Guid classGuid, ref IntPtr deviceNotificationHandle)
		{
			// A DEV_BROADCAST_DEVICEINTERFACE header holds information about the request.

			DEV_BROADCAST_DEVICEINTERFACE devBroadcastDeviceInterface = new DEV_BROADCAST_DEVICEINTERFACE();
			IntPtr devBroadcastDeviceInterfaceBuffer;
			Int32 size = 0;

			// Set the parameters in the DEV_BROADCAST_DEVICEINTERFACE structure.

			// Set the size.

			size = Marshal.SizeOf(devBroadcastDeviceInterface);
			devBroadcastDeviceInterface.dbcc_size = size;

			// Request to receive notifications about a class of devices.

			devBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;

			devBroadcastDeviceInterface.dbcc_reserved = 0;

			// Specify the interface class to receive notifications about.

			devBroadcastDeviceInterface.dbcc_classguid = classGuid;

			// Allocate memory for the buffer that holds the DEV_BROADCAST_DEVICEINTERFACE structure.

			devBroadcastDeviceInterfaceBuffer = Marshal.AllocHGlobal(size);

			// Copy the DEV_BROADCAST_DEVICEINTERFACE structure to the buffer.
			// Set fDeleteOld True to prevent memory leaks.

			Marshal.StructureToPtr(devBroadcastDeviceInterface, devBroadcastDeviceInterfaceBuffer, true);

			// ***
			//  API function

			//  summary
			//  Request to receive notification messages when a device in an interface class
			//  is attached or removed.

			//  parameters 
			//  Handle to the window that will receive device events.
			//  Pointer to a DEV_BROADCAST_DEVICEINTERFACE to specify the type of 
			//  device to send notifications for.
			//  DEVICE_NOTIFY_WINDOW_HANDLE indicates the handle is a window handle.

			//  Returns
			//  Device notification handle or NULL on failure.
			// ***

			deviceNotificationHandle = RegisterDeviceNotification(formHandle, devBroadcastDeviceInterfaceBuffer, DEVICE_NOTIFY_WINDOW_HANDLE);

			// Marshal data from the unmanaged block devBroadcastDeviceInterfaceBuffer to
			// the managed object devBroadcastDeviceInterface

			Marshal.PtrToStructure(devBroadcastDeviceInterfaceBuffer, devBroadcastDeviceInterface);

			// Free the memory allocated previously by AllocHGlobal.

			Marshal.FreeHGlobal(devBroadcastDeviceInterfaceBuffer);

			if ((deviceNotificationHandle.ToInt32() == IntPtr.Zero.ToInt32()))
			{
				return false;
			}
			else
			{
				return true;
			}
		}

		///  <summary>
		///  Requests to stop receiving notification messages when a device in an
		///  interface class is attached or removed.
		///  </summary>
		///  
		///  <param name="deviceNotificationHandle"> handle returned previously by
		///  RegisterDeviceNotification. </param>

		internal void StopReceivingDeviceNotifications(IntPtr deviceNotificationHandle)
		{
			// ***
			//  API function

			//  summary
			//  Stop receiving notification messages.

			//  parameters
			//  Handle returned previously by RegisterDeviceNotification.  

			//  returns
			//  True on success.
			// ***

			//  Ignore failures.

			DeviceManagement.UnregisterDeviceNotification(deviceNotificationHandle);
		}
	}
}





